/*
 * @(#)AbstractAtnaClient.java
 * Date 2013-11-03
 * Version 1.0
 * Author Jim Horner
 * Copyright (c)2012
 */


package us.hornerscorners.lamppost.atna.client;

import java.math.BigInteger;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.nesscomputing.syslog4j.Syslog;
import com.nesscomputing.syslog4j.SyslogConfigIF;
import com.nesscomputing.syslog4j.SyslogFacility;
import com.nesscomputing.syslog4j.SyslogIF;

import com.nesscomputing.syslog4j.util.SyslogUtility;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import us.hornerscorners.lamppost.atna.client.constant.LoggingActor;

import us.hornerscorners.lamppost.atna.client.utils.AtnaUtils;

import us.hornerscorners.lamppost.ihecommon.config.AtnaConfig;
import us.hornerscorners.lamppost.ihecommon.config.AtnaProtocolType;

import us.hornerscorners.lamppost.model.audit.AuditMessage;

import us.hornerscorners.lamppost.model.audit.AuditMessage.ActiveParticipant;

import us.hornerscorners.lamppost.model.audit.CodedValueType;
import us.hornerscorners.lamppost.model.audit.EventIdentificationType;

import us.hornerscorners.lamppost.utils.MarshallUtils;


/**
 * Class description
 *
 *
 * @version        v1.0, 2013-11-03
 * @author         Jim Horner
 */
public abstract class AbstractAtnaClient implements IAtnaClient {

    /** Field description */
    private final AtnaConfig atnaConfig;

    /** Field description */
    private final AtnaContext atnaContext;

    /** Field description */
    private final boolean enabled;

    /** Field description */
    private final Logger logger = LoggerFactory.getLogger(getClass());

    /** Field description */
    private final String protocol;

    /**
     * Constructs ...
     *
     * @param atnaContext
     * @param atnaConfig
     * @param protocol
     */
    public AbstractAtnaClient(AtnaContext atnaContext, AtnaConfig atnaConfig,
                              AtnaProtocolType protocol) {

        super();
        this.atnaContext = atnaContext;
        this.atnaConfig = atnaConfig;
        this.protocol = String.format("atna-%s", protocol.name());
        this.enabled = atnaConfig.isClientEnabled();
    }

    /**
     * Constructs ...
     *
     *
     * @param actor
     * @param eventType
     */
    private AuditMessage createApplicationActivity(LoggingActor actor,
            CodedValueType eventType) {

        AuditMessage result = new AuditMessage();

        EventIdentificationType event = new EventIdentificationType();

        event.setEventID(AtnaUtils.createCodedValue("110100", "DCM",
                "Application Activity"));
        event.setEventActionCode("E");
        event.setEventDateTime(AtnaUtils.createTimeStamp());
        event.setEventOutcomeIndicator(BigInteger.ONE);
        event.getEventTypeCode().add(eventType);
        result.setEventIdentification(event);

        ActiveParticipant app = new ActiveParticipant();

        app.setUserID(actor.getActor());
        app.setUserName(this.atnaContext.getProcessId());
        app.setUserIsRequestor(Boolean.TRUE);
        app.getRoleIDCode().add(AtnaUtils.createCodedValue("110150", "DCM",
                "Application"));
        app.setNetworkAccessPointID(this.atnaContext.getIpAddress());
        app.setNetworkAccessPointTypeCode(Short.valueOf("2"));
        result.getActiveParticipant().add(app);

        ActiveParticipant user = new ActiveParticipant();

        user.setUserID(this.atnaContext.getUserName());
        user.setUserName(String.format("%s@%s", this.atnaContext.getUserName(),
                                       this.atnaContext.getHostName()));
        user.setNetworkAccessPointID(this.atnaContext.getIpAddress());
        user.setNetworkAccessPointTypeCode(Short.valueOf("2"));

        user.setUserIsRequestor(Boolean.TRUE);
        user.getRoleIDCode().add(AtnaUtils.createCodedValue("110151", "DCM",
                "Application Launcher"));
        result.getActiveParticipant().add(user);

        return result;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    protected AtnaConfig getAtnaConfig() {

        return this.atnaConfig;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    @Override
    public AtnaContext getAtnaContext() {
        return atnaContext;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    public String getProtocol() {

        return this.protocol;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    @Override
    public boolean isEnabled() {

        return this.enabled;
    }

    /**
     * Method description
     *
     *
     * @param actors
     */
    @Override
    public void sendApplicationStart(List<LoggingActor> actors) {

        CodedValueType eventType = AtnaUtils.createCodedValue("110120", "DCM",
                                       "Application Start");

        List<AuditMessage> messages = new ArrayList<>();

        for (LoggingActor actor : actors) {
            messages.add(createApplicationActivity(actor, eventType));
        }

        sendAuditEvents(messages);
    }

    /**
     * Method description
     *
     *
     * @param actor
     */
    @Override
    public void sendApplicationStart(LoggingActor actor) {

        sendApplicationStart(Collections.singletonList(actor));
    }

    /**
     * Method description
     *
     *
     * @param actors
     */
    @Override
    public void sendApplicationStop(List<LoggingActor> actors) {

        CodedValueType eventType = AtnaUtils.createCodedValue("110121", "DCM",
                                       "Application Stop");

        List<AuditMessage> messages = new ArrayList<>();

        for (LoggingActor actor : actors) {
            messages.add(createApplicationActivity(actor, eventType));
        }

        sendAuditEvents(messages);
    }

    /**
     * Method description
     *
     *
     * @param actor
     */
    @Override
    public void sendApplicationStop(LoggingActor actor) {

        sendApplicationStop(Collections.singletonList(actor));
    }

    /**
     * Method description
     *
     *
     * @param auditMessage
     */
    @Override
    public void sendAuditEvent(AuditMessage auditMessage) {

        sendAuditEvents(Collections.singletonList(auditMessage));
    }

    /**
     * Method description
     *
     *
     * @param messages
     */
    @Override
    public void sendAuditEvents(List<AuditMessage> messages) {

        logger.debug("sendAuditEvent(s)");

        if (isEnabled()) {

            SyslogIF syslog = Syslog.getInstance(getProtocol());

            for (AuditMessage message : messages) {

                try {

                    String xml = MarshallUtils.marshall(message, AuditMessage.class,
                                     false);

                    logger.debug("Sending message [{}].", xml);

                    // severity of 5
                    syslog.notice(xml);

                } catch (Exception e) {

                    logger.error("Unable to send auditMesasge.", e);
                }
            }

            syslog.flush();

        } else {

            logger.info("ATNA disabled, not sending message(s).");
        }
    }

    /**
     * Method description
     *
     */
    @Override
    public void shutdown() {

        logger.info("Shutting down ATNA client [{}].", this.protocol);

        SyslogIF syslog = Syslog.getInstance(this.protocol);

        syslog.flush();
        syslog.shutdown();
        SyslogUtility.sleep(300L);
    }

    /**
     * Method description
     *
     *
     * @param sysconfig
     */
    protected void startup(SyslogConfigIF sysconfig) {

        sysconfig.setHost(this.atnaConfig.getClientIP());
        sysconfig.setPort(this.atnaConfig.getPort());

        // set to 10
        sysconfig.setFacility(SyslogFacility.authpriv);
        sysconfig.setThrowExceptionOnWrite(true);
        sysconfig.setThrowExceptionOnInitialize(true);

        sysconfig.setTruncateMessage(false);
        sysconfig.setMaxMessageLength(99999);
        
        SyslogIF syslog = Syslog.createInstance(getProtocol(), sysconfig);

        syslog.setMessageProcessor(new AtnaMessageProcessor(this.atnaContext));
    }
}
